#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <functional>
#include <unordered_map>
#include <cstring>
#include "log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Comm.hpp"
class Connection;
class TcpServer;
using func_t = std::function<void(std::shared_ptr<Connection>)>;
uint32_t EVENT_IN = (EPOLLIN | EPOLLET);   // 读事件
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET); // 写事件

class Connection
{
public:
    Connection(int sock, std::shared_ptr<TcpServer> tcp_server_ptr) : _sock(sock), _tcp_server_ptr(tcp_server_ptr)
    {
    }
    // 设置回调函数
    void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    int SockFd()
    {
        return _sock;
    }
    ~Connection()
    {
    }

private:
    int _sock;
    std::string _inbuffer; // 每个套接字的输入缓冲区，有缺陷，无法处理二进制数据
    std::string _outbuffer;

public:
    func_t _recv_cb; // 读回调函数
    func_t _send_cb;
    func_t _except_cb;

    // 指向tcpserver的回指指针
    std::shared_ptr<TcpServer> _tcp_server_ptr;
};

class TcpServer : public nocopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port)
        : _port(port), _listensocket_ptr(new Sock()), _epoller_ptr(new Epoller()), _quit(true)
    {
    }

    void Init()
    {
        _listensocket_ptr->Socket(); // 创建套接字
        SetNonBlockOrDie(_listensocket_ptr->Fd());
        _listensocket_ptr->Bind(_port); // 绑定套接字
        _listensocket_ptr->Listen();
        lg(Info, "create listen socket success :%d\n", _listensocket_ptr->Fd());
        // 首先把监听套接字和读事件添加进连接容器
        AddConnection(_listensocket_ptr->Fd(), EVENT_IN,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);
    }
    // 添加连接结构体
    // 添加到内核和connections对象里的是哪个套接字、添加哪个事件、执行什么操作
    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb)
    {

        // 1.将listensock添加到epoll中，本质是将listensock和它关心的事件（读事件：建立连接）添加到内核epoll模型的红黑树中
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event); // 关心监听套接字的连接事件（读事件）

        // 2.给所有sock建立一个connection对象                  创建一个Connection对象
        std::shared_ptr<Connection> new_connect = std::make_shared<Connection>(sock, (std::shared_ptr<TcpServer>)(this));

        // 3.添加到connections容器，关联fd和连接结构体
        _connections.insert(std::make_pair(sock, new_connect));
    }
    void Accepter(std::shared_ptr<Connection> connection)
    {
        while (true)
        {
            struct sockaddr_in peer; // 存储远端连接属性
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->SockFd(), (sockaddr *)&peer, &len);
            if (sock > 0)
            {
                // 套接字设置为非阻塞
                SetNonBlockOrDie(sock);
                AddConnection(sock, EVENT_IN, nullptr, nullptr, nullptr);
            }
            else
            {
                if (errno == EWOULDBLOCK) // 说明操作被阻塞
                    break;
                else if (errno == EINTR) // 数据正读取，但由于信号触发导致中断
                    continue;
                else
                    break;
            }
        }
    }

    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end()) // 在连接结构体数组中没找到该fd
            return false;
        else
            return true;
    }
    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollerWait(revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events; // 已经就绪的事件
            int sock = revs[i].data.fd;       // 已经就绪的文件描述符
            // 统一把读写异常转换为读写问题
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT);
            if (events & EPOLLHUP) // 证明读到文件末尾或一个远端连接写端关闭
                events |= (EPOLLIN | EPOLLOUT);
            // 如果读事件就绪并且对应的文件描述符是安全的
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
                if (_connections[sock]->_recv_cb)                     // 如果回调函数已经被设置
                    _connections[sock]->_recv_cb(_connections[sock]); // 传入一个连接结构体作为参数
            if ((events & EPOLLOUT) && IsConnectionSafe(sock))
                if (_connections[sock]->_send_cb) // 如果回调函数已经被设置
                    _connections[sock]->_send_cb(_connections[sock]);
        }
    }
    void Loop()
    {
        _quit = false;

        while (!_quit)
        {
            Dispatcher(3000);
        }
        _quit = true;
    }
    void PrintConnection()
    {
        std::cout << "_connections fd list: ";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->SockFd();
        }
        std::cout << std::endl;
    }
    ~TcpServer()
    {
    }

private:
    std::shared_ptr<Sock> _listensocket_ptr;
    std::shared_ptr<Epoller> _epoller_ptr;
    uint16_t _port;
    // key value型，可根据int找到对应的value
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // 管理所有连接结构体的容器,文件描述符和连接结构体关联
    bool _quit;                                                        // 默认是true，表示服务器没有启动
    struct epoll_event revs[num];
};